home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / sh-utils.12 / sh-utils / sh-utils-1.12 / src / who.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  12.5 KB  |  595 lines

  1. /* GNU's users/who.
  2.    Copyright (C) 92, 93, 1994 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by jla; revised by djm */
  19.  
  20. /* Output format:
  21.    name [state] line time [idle] host
  22.    state: -T
  23.    name, line, time: not -q
  24.    idle: -u
  25.  
  26.    Options:
  27.    -m        Same as 'who am i', for POSIX.
  28.    -q        Only user names and # logged on; overrides all other options.
  29.    -s        Name, line, time (default).
  30.    -i, -u    Idle hours and minutes; '.' means active in last minute;
  31.         'old' means idle for >24 hours.
  32.    -H        Print column headings at top.
  33.    -w, -T    -s plus mesg (+ or -, or ? if bad line). */
  34.  
  35. #include <config.h>
  36. #include <stdio.h>
  37. #include <sys/types.h>
  38.  
  39. #ifdef HAVE_UTMPX_H
  40. #include <utmpx.h>
  41. #define STRUCT_UTMP struct utmpx
  42. #else
  43. #include <utmp.h>
  44. #define STRUCT_UTMP struct utmp
  45. #endif
  46.  
  47. #include <time.h>
  48. #include <getopt.h>
  49. #ifdef HAVE_SYS_PARAM_H
  50. #include <sys/param.h>
  51. #endif
  52.  
  53. #include "system.h"
  54. #include "version.h"
  55. #include "safe-stat.h"
  56.  
  57. #if !defined (UTMP_FILE) && defined (_PATH_UTMP)    /* 4.4BSD.  */
  58. #define UTMP_FILE _PATH_UTMP
  59. #endif
  60.  
  61. #if defined (UTMPX_FILE)    /* Solaris, SysVr4 */
  62. #undef  UTMP_FILE
  63. #define UTMP_FILE UTMPX_FILE
  64. #endif
  65.  
  66. #ifndef UTMP_FILE
  67. #define UTMP_FILE "/etc/utmp"
  68. #endif
  69.  
  70. #ifndef MAXHOSTNAMELEN
  71. #define MAXHOSTNAMELEN 64
  72. #endif
  73.  
  74. #ifndef S_IWGRP
  75. #define S_IWGRP 020
  76. #endif
  77.  
  78. #ifdef WHO
  79. #define COMMAND_NAME "who"
  80. #else
  81. #ifdef USERS
  82. #define COMMAND_NAME "users"
  83. #else
  84.  error You must define one of WHO and USERS.
  85. #endif /* USERS */
  86. #endif /* WHO */
  87.  
  88. char *xmalloc ();
  89. void error ();
  90. char *ttyname ();
  91. int gethostname ();
  92.  
  93. static int read_utmp ();
  94. #ifdef WHO
  95. static const char *idle_string ();
  96. static STRUCT_UTMP *search_entries ();
  97. static void print_entry ();
  98. static void print_heading ();
  99. static void scan_entries ();
  100. static void who_am_i ();
  101. #endif /* WHO */
  102. static void list_entries ();
  103. static void usage ();
  104. static void who ();
  105.  
  106. /* The name this program was run with. */
  107. char *program_name;
  108.  
  109. /* If non-zero, display usage information and exit.  */
  110. static int show_help;
  111.  
  112. /* If non-zero, print the version on standard output and exit.  */
  113. static int show_version;
  114.  
  115. #ifdef WHO
  116. /* If nonzero, display only a list of usernames and count of
  117.    the users logged on.
  118.    Ignored for `who am i'. */
  119. static int short_list;
  120.  
  121. /* If nonzero, display the hours:minutes since each user has touched
  122.    the keyboard, or "." if within the last minute, or "old" if
  123.    not within the last day. */
  124. static int include_idle;
  125.  
  126. /* If nonzero, display a line at the top describing each field. */
  127. static int include_heading;
  128.  
  129. /* If nonzero, display a `+' for each user if mesg y, a `-' if mesg n,
  130.    or a `?' if their tty cannot be statted. */
  131. static int include_mesg;
  132. #endif /* WHO */
  133.  
  134. static struct option const longopts[] =
  135. {
  136. #ifdef WHO
  137.   {"count", no_argument, NULL, 'q'},
  138.   {"idle", no_argument, NULL, 'u'},
  139.   {"heading", no_argument, NULL, 'H'},
  140.   {"message", no_argument, NULL, 'T'},
  141.   {"mesg", no_argument, NULL, 'T'},
  142.   {"writable", no_argument, NULL, 'T'},
  143. #endif /* WHO */
  144.   {"help", no_argument, &show_help, 1},
  145.   {"version", no_argument, &show_version, 1},
  146.   {NULL, 0, NULL, 0}
  147. };
  148.  
  149. void
  150. main (argc, argv)
  151.      int argc;
  152.      char **argv;
  153. {
  154.   int optc, longind;
  155. #ifdef WHO
  156.   int my_line_only = 0;
  157. #endif /* WHO */
  158.  
  159.   program_name = argv[0];
  160.  
  161. #ifdef WHO
  162.   while ((optc = getopt_long (argc, argv, "imqsuwHT", longopts, &longind))
  163. #else
  164.   while ((optc = getopt_long (argc, argv, "", longopts, &longind))
  165. #endif /* WHO */
  166.      != EOF)
  167.     {
  168.       switch (optc)
  169.     {
  170.     case 0:
  171.       break;
  172.  
  173. #ifdef WHO
  174.     case 'm':
  175.       my_line_only = 1;
  176.       break;
  177.  
  178.     case 'q':
  179.       short_list = 1;
  180.       break;
  181.  
  182.     case 's':
  183.       break;
  184.  
  185.     case 'i':
  186.     case 'u':
  187.       include_idle = 1;
  188.       break;
  189.  
  190.     case 'H':
  191.       include_heading = 1;
  192.       break;
  193.  
  194.     case 'w':
  195.     case 'T':
  196.       include_mesg = 1;
  197.       break;
  198. #endif /* WHO */
  199.  
  200.     default:
  201.       error (0, 0, "too many arguments");
  202.       usage (1);
  203.     }
  204.     }
  205.  
  206.   if (show_version)
  207.     {
  208.       printf ("%s - %s\n", COMMAND_NAME, version_string);
  209.       exit (0);
  210.     }
  211.  
  212.   if (show_help)
  213.     usage (0);
  214.  
  215.   switch (argc - optind)
  216.     {
  217.     case 0:            /* who */
  218. #ifdef WHO
  219.       if (my_line_only)
  220.     who_am_i (UTMP_FILE);
  221.       else
  222. #endif /* WHO */
  223.     who (UTMP_FILE);
  224.       break;
  225.  
  226.     case 1:            /* who <utmp file> */
  227. #ifdef WHO
  228.       if (my_line_only)
  229.     who_am_i (argv[optind]);
  230.       else
  231. #endif /* WHO */
  232.     who (argv[optind]);
  233.       break;
  234.  
  235. #ifdef WHO
  236.     case 2:            /* who <blurf> <glop> */
  237.       who_am_i (UTMP_FILE);
  238.       break;
  239. #endif /* WHO */
  240.  
  241.     default:            /* lose */
  242.       usage (1);
  243.     }
  244.  
  245.   exit (0);
  246. }
  247.  
  248. static STRUCT_UTMP *utmp_contents;
  249.  
  250. /* Display a list of who is on the system, according to utmp file FILENAME. */
  251.  
  252. static void
  253. who (filename)
  254.      char *filename;
  255. {
  256.   int users;
  257.  
  258.   users = read_utmp (filename);
  259. #ifdef WHO
  260.   if (short_list)
  261.     list_entries (users);
  262.   else
  263.     scan_entries (users);
  264. #else
  265. #ifdef USERS
  266.   list_entries (users);
  267. #endif /* USERS */
  268. #endif /* WHO */
  269. }
  270.  
  271. /* Read the utmp file FILENAME into UTMP_CONTENTS and return the
  272.    number of entries it contains. */
  273.  
  274. static int
  275. read_utmp (filename)
  276.      char *filename;
  277. {
  278.   FILE *utmp;
  279.   struct stat file_stats;
  280.   int n_read;
  281.   size_t size;
  282.  
  283.   utmp = fopen (filename, "r");
  284.   if (utmp == NULL)
  285.     error (1, errno, "%s", filename);
  286.  
  287.   fstat (fileno (utmp), &file_stats);
  288.   size = file_stats.st_size;
  289.   if (size > 0)
  290.     utmp_contents = (STRUCT_UTMP *) xmalloc (size);
  291.   else
  292.     {
  293.       fclose (utmp);
  294.       return 0;
  295.     }
  296.  
  297.   /* Use < instead of != in case the utmp just grew.  */
  298.   n_read = fread (utmp_contents, 1, size, utmp);
  299.   if (ferror (utmp) || fclose (utmp) == EOF
  300.       || n_read < size)
  301.     error (1, errno, "%s", filename);
  302.  
  303.   return size / sizeof (STRUCT_UTMP);
  304. }
  305.  
  306. #ifdef WHO
  307. /* Display a line of information about entry THIS. */
  308.  
  309. static void
  310. print_entry (this)
  311.      STRUCT_UTMP *this;
  312. {
  313.   struct stat stats;
  314.   time_t last_change;
  315.   char mesg;
  316.  
  317. #define DEV_DIR_WITH_TRAILING_SLASH "/dev/"
  318. #define DEV_DIR_LEN (sizeof (DEV_DIR_WITH_TRAILING_SLASH) - 1)
  319.  
  320.   char line[sizeof (this->ut_line) + DEV_DIR_LEN + 1];
  321.  
  322.   /* Copy ut_line into LINE, prepending `/dev/' if ut_line is not
  323.      already an absolute pathname.  Some system may put the full,
  324.      absolute pathname in ut_line.  */
  325.   if (this->ut_line[0] == '/')
  326.     {
  327.       strncpy (line, this->ut_line, sizeof (this->ut_line));
  328.       line[sizeof (this->ut_line)] = '\0';
  329.     }
  330.   else
  331.     {
  332.       strcpy(line, DEV_DIR_WITH_TRAILING_SLASH);
  333.       strncpy (line + DEV_DIR_LEN, this->ut_line, sizeof (this->ut_line));
  334.       line[DEV_DIR_LEN + sizeof (this->ut_line)] = '\0';
  335.     }
  336.  
  337.   if (SAFE_STAT (line, &stats) == 0)
  338.     {
  339.       mesg = (stats.st_mode & S_IWGRP) ? '+' : '-';
  340.       last_change = stats.st_atime;
  341.     }
  342.   else
  343.     {
  344.       mesg = '?';
  345.       last_change = 0;
  346.     }
  347.   
  348.   printf ("%-8.*s", (int) sizeof (this->ut_name), this->ut_name);
  349.   if (include_mesg)
  350.     printf ("  %c  ", mesg);
  351.   printf (" %-8.*s", (int) sizeof (this->ut_line), this->ut_line);
  352.  
  353. #ifdef HAVE_UTMPX_H
  354.   printf (" %-12.12s", ctime (&this->ut_tv.tv_sec) + 4);
  355. #else
  356.   printf (" %-12.12s", ctime (&this->ut_time) + 4);
  357. #endif
  358.  
  359.   if (include_idle)
  360.     {
  361.       if (last_change)
  362.     printf (" %s", idle_string (last_change));
  363.       else
  364.     printf ("   .  ");
  365.     }
  366. #ifdef HAVE_UT_HOST
  367.   if (this->ut_host[0])
  368.     printf (" (%-.*s)", (int) sizeof (this->ut_host), this->ut_host);
  369. #endif
  370.  
  371.   putchar ('\n');
  372. }
  373. #endif /* WHO */
  374.  
  375. #if defined (WHO) || defined (USERS)
  376. /* Print the username of each valid entry and the number of valid entries
  377.    in `utmp_contents', which should have N elements. */
  378.  
  379. static void
  380. list_entries (n)
  381.      int n;
  382. {
  383.   register STRUCT_UTMP *this = utmp_contents;
  384.   register int entries = 0;
  385.  
  386.   while (n--)
  387.     {
  388.       if (this->ut_name[0]
  389. #ifdef USER_PROCESS
  390.       && this->ut_type == USER_PROCESS
  391. #endif
  392.      )
  393.     {
  394.       char trimmed_name[sizeof (this->ut_name) + 1];
  395.       int i;
  396.  
  397.       strncpy (trimmed_name, this->ut_name, sizeof (this->ut_name));
  398.       trimmed_name[sizeof (this->ut_name)] = ' ';
  399.       for (i = 0; i <= sizeof (this->ut_name); i++)
  400.         {
  401.           if (trimmed_name[i] == ' ')
  402.         break;
  403.         }
  404.       trimmed_name[i] = '\0';
  405.  
  406.       printf ("%s ", trimmed_name);
  407.       entries++;
  408.     }
  409.       this++;
  410.     }
  411. #ifdef WHO
  412.   printf ("\n# users=%u\n", entries);
  413. #else
  414.   putchar('\n');
  415. #endif /* WHO */
  416. }
  417. #endif /* defined (WHO) || defined (USERS) */
  418.  
  419. #ifdef WHO
  420.  
  421. static void
  422. print_heading ()
  423. {
  424.   printf ("%-8s ", "USER");
  425.   if (include_mesg)
  426.     printf ("MESG ");
  427.   printf ("%-8s ", "LINE");
  428.   printf ("LOGIN-TIME   ");
  429.   if (include_idle)
  430.     printf ("IDLE  ");
  431.   printf ("FROM\n");
  432. }
  433.  
  434. /* Display `utmp_contents', which should have N entries. */
  435.  
  436. static void
  437. scan_entries (n)
  438.      int n;
  439. {
  440.   register STRUCT_UTMP *this = utmp_contents;
  441.  
  442.   if (include_heading)
  443.     print_heading ();
  444.  
  445.   while (n--)
  446.     {
  447.       if (this->ut_name[0]
  448. #ifdef USER_PROCESS
  449.       && this->ut_type == USER_PROCESS
  450. #endif
  451.      )
  452.     print_entry (this);
  453.       this++;
  454.     }
  455. }
  456.  
  457. /* Search `utmp_contents', which should have N entries, for
  458.    an entry with a `ut_line' field identical to LINE.
  459.    Return the first matching entry found, or NULL if there
  460.    is no matching entry. */
  461.  
  462. static STRUCT_UTMP *
  463. search_entries (n, line)
  464.      int n;
  465.      char *line;
  466. {
  467.   register STRUCT_UTMP *this = utmp_contents;
  468.  
  469.   while (n--)
  470.     {
  471.       if (this->ut_name[0]
  472. #ifdef USER_PROCESS
  473.       && this->ut_type == USER_PROCESS
  474. #endif
  475.       && !strncmp (line, this->ut_line, sizeof (this->ut_line)))
  476.     return this;
  477.       this++;
  478.     }
  479.   return NULL;
  480. }
  481.  
  482. /* Display the entry in utmp file FILENAME for this tty on standard input,
  483.    or nothing if there is no entry for it. */
  484.  
  485. static void
  486. who_am_i (filename)
  487.      char *filename;
  488. {
  489.   register STRUCT_UTMP *utmp_entry;
  490.   char hostname[MAXHOSTNAMELEN + 1];
  491.   char *tty;
  492.  
  493.   if (gethostname (hostname, MAXHOSTNAMELEN + 1))
  494.     *hostname = 0;
  495.  
  496.   if (include_heading)
  497.     {
  498.       printf ("%*s ", (int) strlen (hostname), " ");
  499.       print_heading ();
  500.     }
  501.  
  502.   tty = ttyname (0);
  503.   if (tty == NULL)
  504.     return;
  505.   tty += 5;            /* Remove "/dev/".  */
  506.   
  507.   utmp_entry = search_entries (read_utmp (filename), tty);
  508.   if (utmp_entry == NULL)
  509.     return;
  510.  
  511.   printf ("%s!", hostname);
  512.   print_entry (utmp_entry);
  513. }
  514.  
  515. /* Return a string representing the time between WHEN and the time
  516.    that this function is first run. */
  517.  
  518. static const char *
  519. idle_string (when)
  520.      time_t when;
  521. {
  522.   static time_t now = 0;
  523.   static char idle[10];
  524.   time_t seconds_idle;
  525.  
  526.   if (now == 0)
  527.     time (&now);
  528.  
  529.   seconds_idle = now - when;
  530.   if (seconds_idle < 60)    /* One minute. */
  531.     return "  .  ";
  532.   if (seconds_idle < (24 * 60 * 60)) /* One day. */
  533.     {
  534.       sprintf (idle, "%02d:%02d",
  535.            (int) (seconds_idle / (60 * 60)),
  536.            (int) ((seconds_idle % (60 * 60)) / 60));
  537.       return (const char *) idle;
  538.     }
  539.   return " old ";
  540. }
  541.  
  542. static void
  543. usage (status)
  544.      int status;
  545. {
  546.   if (status != 0)
  547.     fprintf (stderr, "Try `%s --help' for more information.\n",
  548.          program_name);
  549.   else
  550.     {
  551.       printf ("Usage: %s [OPTION]... [ FILE | ARG1 ARG2 ]\n", program_name);
  552.       printf ("\
  553. \n\
  554.   -H, --heading     print line of column headings\n\
  555.   -T, -w, --mesg    add user's message status as +, - or ?\n\
  556.   -i, -u, --idle    add user idle time as HOURS:MINUTES, . or old\n\
  557.   -m                only hostname and user associated with stdin\n\
  558.   -q, --count       all login names and number of users logged on\n\
  559.   -s                (ignored)\n\
  560.       --help        display this help and exit\n\
  561.       --message     same as -T\n\
  562.       --version     output version information and exit\n\
  563.       --writeable   same as -T\n\
  564. \n\
  565. If FILE not given, uses /etc/utmp.  /etc/wtmp as FILE is common.\n\
  566. If ARG1 ARG2 given, -m presumed: `am i' or `mom likes' are usual.\n\
  567. ");
  568.     }
  569.   exit (status);
  570. }
  571. #endif /* WHO */
  572.  
  573. #if defined (USERS)
  574. static void
  575. usage (status)
  576.      int status;
  577. {
  578.   if (status != 0)
  579.     fprintf (stderr, "Try `%s --help' for more information.\n",
  580.          program_name);
  581.   else
  582.     {
  583.       printf ("Usage: %s [OPTION]... [ FILE ]\n", program_name);
  584.       printf ("\
  585. \n\
  586.       --help        display this help and exit\n\
  587.       --version     output version information and exit\n\
  588. \n\
  589. If FILE not given, uses /etc/utmp.  /etc/wtmp as FILE is common.\n\
  590. ");
  591.     }
  592.   exit (status);
  593. }
  594. #endif /* USERS */
  595.